import json
import google.protobuf.json_format as json_format

from coin.exchange.okex.kr_rest.product import OkexProduct, OkexCurrency
from coin.exchange.base.kr_rest.private_client_base import (PrivateClientBase,
                                                            ExWalletPrivateClientBase)
from coin.exchange.okex_v5.kr_rest.native_private_client import (
    OkexNativePrivateClient,
)
from coin.proto.coin_query_pb2 import (
    AccountBalance,
    CurrencyTransferBundle,
    CurrencyTransferElement
)
from coin.exchange.okex_v5.kr_rest.product_info import okex_spot_product_info 
from cc.appcoin2.support.order.py_parser import PyOrderParser
from coin2.exchange.order.system_pb2 import OrderSystemConfig
from coin2.exchange.symbology_pb2 import ProductConfig


class OkexPrivateParser(object):
  ProductType = OkexProduct

  @staticmethod
  def parse_transfers(withdraw_update_msg, deposite_update_msg, currency):
    withdraw_status_map = {
        '-3': 'CANCEL_ACCEPTED',
        '-2': 'CANCEL_CONFIRMED',
        '-1': 'TRANSFER_REJECTED',
        '0': 'TRANSFER_ACCEPTED',
        '1': 'TRANSFER_ACCEPTED',
        '2': 'TRANSFER_CONFIRMED',
        '3': 'TRANSFER_ACCEPTED',
        '4': 'TRANSFER_ACCEPTED',
        '5': 'TRANSFER_ACCEPTED'
    }
    deposite_status_map = {
        '0': 'TRANSFER_ACCEPTED',
        '1': 'TRANSFER_ACCEPTED',
        '2': 'TRANSFER_CONFIRMED'
    }
    transfer_list = []
    for transfer_elem in withdraw_update_msg['data']:
      assert currency.currency == transfer_elem['ccy']
      transfer = CurrencyTransferElement(
          currency=currency.currency,
          type='WITHDRAW',
          from_address=transfer_elem['from'],
          to_address=transfer_elem['to'],
          qty=float(transfer_elem['amt']),
          fee=float(transfer_elem['fee']),
          external_transfer_id=transfer_elem['txId'],
          status=withdraw_status_map[transfer_elem['state']],
          create_timestamp=int(int(transfer_elem['ts']) * 1e6))
      transfer_list.append(transfer)
    for transfer_elem in deposite_update_msg['data']:
      assert currency.currency == transfer_elem['ccy']
      transfer = CurrencyTransferElement(
          currency=currency.currency,
          type='DEPOSIT',
          from_address=transfer_elem['from'],
          to_address=transfer_elem['to'],
          qty=float(transfer_elem['amt']),
          external_transfer_id=transfer_elem['txId'],
          status=deposite_status_map[transfer_elem['state']],
          create_timestamp=int(int(transfer_elem['ts']) * 1e6))
      transfer_list.append(transfer)
    return CurrencyTransferBundle(exchange='Okex',
                                  market_type='Spot',
                                  currency=currency.currency,
                                  native_currency=currency.native_currency,
                                  each_transfer=transfer_list)


class OkexPrivateClient(PrivateClientBase):
  ProductType = OkexProduct

  def __init__(self, key_file):
    super().__init__()
    self.nprivc = OkexNativePrivateClient(key_file=key_file)
    pi_bundle = okex_spot_product_info._product_infos
    assert pi_bundle.HasField('mea')
    oss_config = OrderSystemConfig(
        mea=pi_bundle.mea,
        products=ProductConfig(norms=[pi.symbol for pi in pi_bundle.product_infos]))
    self._parser = PyOrderParser.from_mea_str(pi_bundle.mea)
    self._parser.add_product(oss_config.SerializeToString())

  def query_account_balance_impl(self):
      update = self.nprivc.query_accounts()
      msg = self._parser.parse_account_balance(json.dumps(update.msg))
      account_balance = AccountBalance()
      json_format.Parse(msg, account_balance)
      update.msg = account_balance
      return update

  def query_account_transfer_impl(self, currency):
    currency = OkexCurrency.FromStr(currency)
    withdraw_update = self.nprivc.query_withdraw_history(currency.currency)
    deposit_update = self.nprivc.query_deposit_history(currency.currency)
    update = deposit_update
    update.msg = OkexPrivateParser.parse_transfers(withdraw_update.msg,
                                                   deposit_update.msg,
                                                   currency)
    return update

  def query_cancel_product_impl(self, product):
    raise NotImplementedError

  def query_fills_impl(self, product):
    raise NotImplementedError

  def query_submit_impl(self, product, side, price, qty):
    raise NotImplementedError


class OkexExWalletPrivateClient(ExWalletPrivateClientBase):
  ProductType = OkexProduct

  def __init__(self, key_file):
    super().__init__()
    self.nprivc = OkexNativePrivateClient(key_file=key_file)
    pi_bundle = okex_spot_product_info._product_infos
    assert pi_bundle.HasField('mea')
    oss_config = OrderSystemConfig(
        mea=pi_bundle.mea,
        products=ProductConfig(norms=[pi.symbol for pi in pi_bundle.product_infos]))
    self._parser = PyOrderParser.from_mea_str(pi_bundle.mea)
    self._parser.add_product(oss_config.SerializeToString())

  def query_account_balance_impl(self):
    update = self.nprivc.query_wallet()
    msg = self._parser.parse_account_balance(json.dumps(update.msg))
    account_balance = AccountBalance()
    json_format.Parse(msg, account_balance)
    update.msg = account_balance
    return update

  def query_account_transfer_impl(self, currency):
    currency = OkexCurrency.FromStr(currency)
    withdraw_update = self.nprivc.query_withdraw_history(currency.currency)
    deposit_update = self.nprivc.query_deposit_history(currency.currency)
    update = deposit_update
    update.msg = OkexPrivateParser.parse_transfers(withdraw_update.msg,
                                                   deposit_update.msg,
                                                   currency)
    return update
